// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/border.h"

#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/views/painter.h"
#include "ui/views/view.h"

namespace views {

namespace {

    // A simple border with different thicknesses on each side and single color.
    class SolidSidedBorder : public Border {
    public:
        SolidSidedBorder(const gfx::Insets& insets, SkColor color);

        // Overridden from Border:
        void Paint(const View& view, gfx::Canvas* canvas) override;
        gfx::Insets GetInsets() const override;
        gfx::Size GetMinimumSize() const override;

    private:
        const gfx::Insets insets_;
        const SkColor color_;

        DISALLOW_COPY_AND_ASSIGN(SolidSidedBorder);
    };

    SolidSidedBorder::SolidSidedBorder(const gfx::Insets& insets, SkColor color)
        : insets_(insets)
        , color_(color)
    {
    }

    void SolidSidedBorder::Paint(const View& view, gfx::Canvas* canvas)
    {
        // Top border.
        canvas->FillRect(gfx::Rect(0, 0, view.width(), insets_.top()), color_);
        // Left border.
        canvas->FillRect(gfx::Rect(0, insets_.top(), insets_.left(),
                             view.height() - insets_.height()),
            color_);
        // Bottom border.
        canvas->FillRect(gfx::Rect(0, view.height() - insets_.bottom(), view.width(),
                             insets_.bottom()),
            color_);
        // Right border.
        canvas->FillRect(gfx::Rect(view.width() - insets_.right(), insets_.top(),
                             insets_.right(), view.height() - insets_.height()),
            color_);
    }

    gfx::Insets SolidSidedBorder::GetInsets() const
    {
        return insets_;
    }

    gfx::Size SolidSidedBorder::GetMinimumSize() const
    {
        return gfx::Size(insets_.width(), insets_.height());
    }

    // A border with a rounded rectangle and single color.
    class RoundedRectBorder : public Border {
    public:
        RoundedRectBorder(int thickness, int corner_radius, SkColor color);

        // Overridden from Border:
        void Paint(const View& view, gfx::Canvas* canvas) override;
        gfx::Insets GetInsets() const override;
        gfx::Size GetMinimumSize() const override;

    private:
        const int thickness_;
        const int corner_radius_;
        const SkColor color_;

        DISALLOW_COPY_AND_ASSIGN(RoundedRectBorder);
    };

    RoundedRectBorder::RoundedRectBorder(int thickness,
        int corner_radius,
        SkColor color)
        : thickness_(thickness)
        , corner_radius_(corner_radius)
        , color_(color)
    {
    }

    void RoundedRectBorder::Paint(const View& view, gfx::Canvas* canvas)
    {
        SkPaint paint;
        paint.setStrokeWidth(thickness_);
        paint.setColor(color_);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setAntiAlias(true);

        float half_thickness = thickness_ / 2.0f;
        gfx::RectF bounds(view.GetLocalBounds());
        bounds.Inset(half_thickness, half_thickness);
        canvas->DrawRoundRect(bounds, corner_radius_, paint);
    }

    gfx::Insets RoundedRectBorder::GetInsets() const
    {
        return gfx::Insets(thickness_, thickness_, thickness_, thickness_);
    }

    gfx::Size RoundedRectBorder::GetMinimumSize() const
    {
        return gfx::Size(thickness_ * 2, thickness_ * 2);
    }

    class EmptyBorder : public Border {
    public:
        explicit EmptyBorder(const gfx::Insets& insets);

        // Overridden from Border:
        void Paint(const View& view, gfx::Canvas* canvas) override;
        gfx::Insets GetInsets() const override;
        gfx::Size GetMinimumSize() const override;

    private:
        const gfx::Insets insets_;

        DISALLOW_COPY_AND_ASSIGN(EmptyBorder);
    };

    EmptyBorder::EmptyBorder(const gfx::Insets& insets)
        : insets_(insets)
    {
    }

    void EmptyBorder::Paint(const View& view, gfx::Canvas* canvas)
    {
    }

    gfx::Insets EmptyBorder::GetInsets() const
    {
        return insets_;
    }

    gfx::Size EmptyBorder::GetMinimumSize() const
    {
        return gfx::Size();
    }

    class BorderPainter : public Border {
    public:
        BorderPainter(Painter* painter, const gfx::Insets& insets);

        // Overridden from Border:
        void Paint(const View& view, gfx::Canvas* canvas) override;
        gfx::Insets GetInsets() const override;
        gfx::Size GetMinimumSize() const override;

    private:
        scoped_ptr<Painter> painter_;
        const gfx::Insets insets_;

        DISALLOW_COPY_AND_ASSIGN(BorderPainter);
    };

    BorderPainter::BorderPainter(Painter* painter, const gfx::Insets& insets)
        : painter_(painter)
        , insets_(insets)
    {
        DCHECK(painter);
    }

    void BorderPainter::Paint(const View& view, gfx::Canvas* canvas)
    {
        Painter::PaintPainterAt(canvas, painter_.get(), view.GetLocalBounds());
    }

    gfx::Insets BorderPainter::GetInsets() const
    {
        return insets_;
    }

    gfx::Size BorderPainter::GetMinimumSize() const
    {
        return painter_->GetMinimumSize();
    }

} // namespace

Border::Border()
{
}

Border::~Border()
{
}

// static
scoped_ptr<Border> Border::NullBorder()
{
    return nullptr;
}

// static
scoped_ptr<Border> Border::CreateSolidBorder(int thickness, SkColor color)
{
    return make_scoped_ptr(new SolidSidedBorder(
        gfx::Insets(thickness, thickness, thickness, thickness), color));
}

// static
scoped_ptr<Border> Border::CreateEmptyBorder(const gfx::Insets& insets)
{
    return make_scoped_ptr(new EmptyBorder(insets));
}

// static
scoped_ptr<Border> Border::CreateRoundedRectBorder(int thickness,
    int corner_radius,
    SkColor color)
{
    return make_scoped_ptr(
        new RoundedRectBorder(thickness, corner_radius, color));
}

// static
scoped_ptr<Border> Border::CreateEmptyBorder(int top,
    int left,
    int bottom,
    int right)
{
    return CreateEmptyBorder(gfx::Insets(top, left, bottom, right));
}

// static
scoped_ptr<Border> Border::CreateSolidSidedBorder(int top,
    int left,
    int bottom,
    int right,
    SkColor color)
{
    return make_scoped_ptr(new SolidSidedBorder(
        gfx::Insets(top, left, bottom, right), color));
}

// static
scoped_ptr<Border> Border::CreateBorderPainter(Painter* painter,
    const gfx::Insets& insets)
{
    return make_scoped_ptr(new BorderPainter(painter, insets));
}

} // namespace views
